# I am *so. done.* writing vector tables by hand. Help me out, Python?
import sys

# Don't run the script if the wrong # of arguments are passed in.
if not len( sys.argv ) == 3:
  print( 'Usage: python generate_vt.py <MCU_class> <MCU_core>\n' +
         'Example: python generate_vt.py STM32WBxx cortex-m4' )
  sys.exit( 1 )

# Array to hold relevant lines in the device header file.
irq_lines = []
# State-tracking variable for the header file parsing.
irq_appending = False
# Filename to write the vector table to.
vt_fn = sys.argv[ 1 ] + '_vt.S'
# Device header filename to read the interrupt information from.
dev_hdr_fp = 'device_headers/' + sys.argv[1].lower() + '.h'

# Open the device header file, and read its interrupt definition
# enum into the 'irq_lines' array.
with open( dev_hdr_fp, 'r' ) as dev_hdr:
  for line in dev_hdr:
    if irq_appending:
      irq_lines.append( line )
      if 'IRQn_Type' in line:
        # End of the interrupt enum; no need to keep reading.
        break
    elif 'Interrupt Number Definition' in line:
      # Near the start of the interrupt enum; start reading.
      irq_appending = True

# Dictionary to hold [ interrupt_index : interrupt_name ] pairs.
irq_dict = { }

# Run through the interrupt definitions enum, and parse each relevant
# line into a (key, value) pair in the 'irq_dict' variable.
for line in irq_lines:
  if '=' in line:
    while '  ' in line:
      line = line.replace( '  ', ' ' )
    line = line.replace( ',', '' )
    blocks = line.split( ' ' )
    irq_dict[ int( blocks[ 3 ] ) ] = blocks[ 1 ]

# Create a sorted list from the dictionary, so that interrupts
# will be in the right order in the vector table.
irq_list = sorted( irq_dict.items() )

# Open the target file and write a vector table.
with open( vt_fn, 'w+' ) as vt_as:
  # Header comment.
  vt_as.write( '/* Autogenerated vector table for ' +
               sys.argv[ 1 ] + ' */\n\n' )
  # Assembler directives.
  vt_as.write( '.syntax unified\n' )
  vt_as.write( '.cpu ' + sys.argv[ 2 ] + '\n' )
  vt_as.write( '.thumb\n\n' )
  # Labels to export to the wider program context.
  vt_as.write( '.global vtable\n' )
  vt_as.write( '.global default_interrupt_handler\n\n' )

  # The vector table definition. Goes into a special 'vector_table'
  # section so that the linker script can ensure it is placed at the
  # very beginning of the chip's memory space.
  vt_as.write( '.type vtable, %object\n' )
  vt_as.write( '.section .vector_table,"a",%progbits\n' )
  vt_as.write( 'vtable:\n' )
  # The first two entries are the 'end of stack' address and the
  # reset handler - neither are present in the device header file.
  vt_as.write( '  .word _estack\n' )
  vt_as.write( '  .word reset_handler\n' )
  # For each number in the range of interrupt indices, check if
  # an interrupt exists at that index. If so, make an entry for it.
  # If not, write a '0' to keep the correct ordering.
  for i in range( irq_list[ 0 ][ 0 ], ( irq_list[ -1 ][ 0 ] + 1 ) ):
    if i in irq_dict:
      vt_as.write( '  .word ' + irq_dict[ i ] + '_handler\n' )
    else:
      vt_as.write( '  .word 0\n' )
  vt_as.write( '\n' )

  # Create weak references for each interrupt handler, so that they
  # point to a dummy handler if the application writer does not
  # define an interrupt handler in their program.
  for i in range( irq_list[ 0 ][ 0 ], ( irq_list[ -1 ][ 0 ] + 1 ) ):
    if i in irq_dict:
      vt_as.write( '  .weak ' + irq_dict[ i ] + '_handler\n' )
      vt_as.write( '  .thumb_set ' + irq_dict[ i ] + '_handler,default_interrupt_handler\n' )

  # Close the definition of the vector table.
  vt_as.write( '.size vtable, .-vtable\n\n' )
  vt_as.write( '.section .text.default_interrupt_handler,"ax",%progbits\n' )

  # Define a default 'dummy' interrupt handler. This is about the same
  # as `while(1) {};`, it's an infinite loop. So if an interrupt is
  # triggered while a handler is not defined, the program will freeze.
  # That is better than accidentally corrupting memory, though.
  vt_as.write( 'default_interrupt_handler:\n' )
  vt_as.write( '  default_interrupt_loop:\n' )
  vt_as.write( '    B default_interrupt_loop\n' )
  vt_as.write( '.size default_interrupt_handler, .-default_interrupt_handler\n' )

# Done.
sys.exit( 0 )
